package org.fujene.memoryblock.blockwise;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import org.fujene.memoryblock.MemoryBlock;
import org.fujene.structure.Constants;
import org.fujene.structure.LookupPointer;
import org.fujene.toolbox.ByteConverter;

public class BlockwiseContentBlock extends MemoryBlock {
	private int opt_length = 0;
	private int opt_supplements = 0;
	private int opt_contentindex = -1;

	public BlockwiseContentBlock(int bodySize, int ID, boolean isAllocate) {
		super(bodySize, ID, Constants.MEMORY_TYPE_CONTENTS, isAllocate);
	}

	private class Contents {
		long ID;
		LookupPointer[] supps;
		int strlen;
		String str;

		// 読み込み用
		public Contents(int from, boolean readStr) {
			supps = new LookupPointer[opt_supplements];
			
			ID = ByteConverter.byteToLong(getBody(from, 8));
			from += 8;

			for (int i = 0; i < opt_supplements; i++) {
				supps[i] = ByteConverter.getPtr(getBody(from, PTR));
				from += PTR;
			}

			if (readStr) {
				strlen = ByteConverter.byteToInt(getBody(from, STRLEN));
				from += STRLEN;

				try {
					str = new String(getBody(from, strlen), "UTF-8");
				} catch (UnsupportedEncodingException e) {
				}
			}
		}

		// 書き込み用
		public Contents(String str, long ID) {
			this.ID = ID;
			
			this.supps = new LookupPointer[opt_supplements];
			for (int i = 0; i < this.supps.length; i++) {
				this.supps[i] = Constants.emptyPointer;
			}

			this.str = str;
			try {
				strlen = str.getBytes("UTF-8").length;
			} catch (UnsupportedEncodingException e) {
			}
		}

		public void setSuppPtrs(LookupPointer ptr, int index) {
			if (supps != null && supps.length < index)
				supps[index] = ptr;
		}

		// 実際に書き込み
		public void write(int from, boolean writeStr) {
			setBody(ByteConverter.longToByte(this.ID, 8), from);
			from += 8;
			
			if(this.supps != null){
				for (int i = 0; i < this.supps.length; i++) {
					setBody(ByteConverter.setPtr(this.supps[i]), from);
					from += PTR;
				}
			}

			if (writeStr) {
				setBody(ByteConverter.intToByte(strlen, STRLEN), from);
				from += STRLEN;
				try {
					setBody(this.str.getBytes("UTF-8"), from);
				} catch (UnsupportedEncodingException e) {
				}
			}
		}
	}

	public String getContent(int ptr) {
		Contents content = new Contents(ptr, true);

		return content.str;
	}

	public void setContent(int ptr, LookupPointer[] suppPtrs, long ID, String str) {
		Contents content = new Contents(str, ID);

		content.supps = suppPtrs;
		
		content.write(ptr, true);

		opt_length++;
	}

	public LookupPointer[] getSuppPtr(int ptr) {
		Contents content = new Contents(ptr, false);

		return content.supps;
	}

	public void setSuppPtr(int ptr, LookupPointer suppPtr, int index) {
		Contents content = new Contents(ptr, false);

		content.setSuppPtrs(suppPtr, index);

		content.write(ptr, false);
	}

	public int getNewPointer(String str) {
		try {
			byte[] strb = str.getBytes("UTF-8");
			int returnPtr = this.bodySize - this.bodyVacancy;
			if(this.bodyVacancy >= 8 + (opt_supplements * PTR) + STRLEN + strb.length){
				this.bodyVacancy -= 8 + (opt_supplements * PTR) + STRLEN + strb.length;
				return returnPtr;
			}else
				return -1;
		} catch (UnsupportedEncodingException e) {
			return -1;
		}
	}
	
	public long getContentID(int ptr){
		Contents content = new Contents(ptr, false);
		
		return content.ID;
	}
	
	public int getLength(){
		return opt_length;
	}
	
	public int getCount(){
		return opt_length;
	}
	
	public static byte[] createByteArray(LookupPointer[] suppPtrs, long ID, String str) throws IOException{
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		
		stream.write(ByteConverter.longToByte(ID, 8));
		for (int i = 0; i < suppPtrs.length; i++) {
			stream.write(ByteConverter.setPtr(suppPtrs[i]));
		}
		
		byte[] strbyte = str.getBytes("UTF-8");
		stream.write(ByteConverter.intToByte(strbyte.length, STRLEN));
		stream.write(strbyte);
		
		return stream.toByteArray();
	}
	
	public byte[] getData() throws IOException{
		ByteArrayOutputStream stream = new ByteArrayOutputStream(this.bodySize + 12); 
		
		stream.write(getOptionByteStream());
		stream.write(this.getBody(0, this.bodySize));
		
		return stream.toByteArray();
	}
	
	public void setData(byte[] array){
		opt_length = ByteConverter.byteToInt(array, 0, 4);
		opt_supplements = ByteConverter.byteToInt(array, 4, 4);
		opt_contentindex = ByteConverter.byteToInt(array, 8, 4);
		
		setBody(array, 8, bodySize, 0);
	}
	
	public int getContentIndex(){
		return opt_contentindex;
	}
	
	@Override
	public int getOptionSize() {
		return 3;
	}

	@Override
	public byte[] getOptionByteStream() {
		ByteArrayOutputStream stream = new ByteArrayOutputStream(12);
		
		try {
			stream.write(ByteConverter.intToByte(opt_length, 4));
			stream.write(ByteConverter.intToByte(opt_supplements, 4));
			stream.write(ByteConverter.intToByte(opt_contentindex, 4));
		} catch (IOException e) {
		}
		
		return stream.toByteArray();
	}

	@Override
	public void setOption(int option, int value) {
		switch(option){
		case 0:
			opt_length = value;
			break;
		case 1:
			opt_supplements = value;
			break;
		case 2:
			opt_contentindex = value;
			break;
		}
	}

	@Override
	public String getClassName() {
		return new String("Block-wise ContentBlock");
	}

}
